package com.innovation.ic.cyz.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.innovation.ic.b1b.framework.util.HttpUtils;
import com.innovation.ic.b1b.framework.util.JwtUtils;
import com.innovation.ic.b1b.framework.util.StringUtils;
import com.innovation.ic.cyz.base.model.cyz.Client;
import com.innovation.ic.cyz.base.pojo.constant.Constants;
import com.innovation.ic.cyz.base.pojo.constant.RedisStorage;
import com.innovation.ic.cyz.base.pojo.global.Context;
import com.innovation.ic.cyz.base.value.config.ErpInterfaceAddressConfig;
import com.innovation.ic.cyz.base.value.config.FilterParamConfig;
import com.innovation.ic.cyz.base.value.config.RunEnvConfig;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 验证header中是否有Sec-WebSocket-Protocol(websocket请求)或extension(http请求)属性，其值为：{“authorization”: clientId}
 */
@Component
public class AccessFilter implements GlobalFilter, Ordered {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private FilterParamConfig config;

    @Resource
    private ErpInterfaceAddressConfig erpInterfaceAddressConfig;

    @Resource
    private RunEnvConfig runEnvConfig;

    private static FilterParamConfig filterParamConfig;

    @PostConstruct
    public void init() {
        filterParamConfig = config;
    }

    /**
     * /**
     * 过滤规则：
     * 1.header中是否有clientId，clientId是否合法。
     * 2.在header中有clientId并且合法的情况下，/cyz-web直接放行，/cyz-admin还要查看header中是否有token(token的开头是“Bearer ”)。
     * 3.在header中有token的情况下，调用erp的登录接口，判断token是否合法。
     *
     * @param exchange
     * @param chain
     * @return
     */
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        // 如果是允许直接通过了路径，则放行
        String path = request.getPath().toString();
        List<String> allowPathList = filterParamConfig.getAllowPathList();
        for (String allowPath : allowPathList) {
            if (path.contains(allowPath)) {
                logger.info("请求【" + path + "】中包含路径【" + allowPath + "】，直接放行");
                return chain.filter(exchange);
            }
        }

        HttpHeaders httpHeaders = request.getHeaders();
        Set<String> httpHeaderKeySet = httpHeaders.keySet();
        logger.info("httpHeaders的值为[{}]", httpHeaders.toString());
        if (null != httpHeaders && !httpHeaders.isEmpty()) {
            // 验证header中的clientId，key为authorization
            Set<String> clientSet = (Set<String>) Context.get(RedisStorage.CLIENT);
            if (clientSet == null) {
                logger.warn("本地的Context中没有{}", filterParamConfig.getAuthorization());
                return null;
            }

            if (!httpHeaderKeySet.contains(filterParamConfig.getAuthorization())) {
                logger.warn("请求的header中没有{}", filterParamConfig.getAuthorization());
                return null;
            } else {
                String clientId = httpHeaders.get(filterParamConfig.getAuthorization()).get(0);
                Iterator clientIterator = clientSet.iterator();
                Client client;
                boolean clientIdExist = false;
                while (clientIterator.hasNext()) {
                    client = JSON.parseObject((String) clientIterator.next(), Client.class);
                    if (clientId.equals(client.getId())) {
                        clientIdExist = true;
                        break;
                    }
                }
                if (!clientIdExist) {
                    logger.warn("header中的clientId参数错误");
                    return null;
                } else {
                    logger.info("header中的clientId参数为{}", clientId);
                }
            }

            // 验证header中的token，key是token
            if (!httpHeaderKeySet.contains(filterParamConfig.getToken())) {
                logger.warn("请求的header中没有{}", filterParamConfig.getToken());
                return null;
            } else {
                String tokenString = httpHeaders.get(filterParamConfig.getToken()).get(0);
                logger.info("header中的Token参数为{}", tokenString);

                // cyz-web校验token是否有效
                if(path.contains(filterParamConfig.getWebPath())){
                    try {
                        JwtUtils.verify(tokenString.split(" ")[1]);
                    } catch (Exception e) {
                        logger.warn("header中的token参数不合法");
                        return null;
                    }
                }

                // cyz-admin校验token是否有效
                if(path.contains(filterParamConfig.getAdminPath())){
                    Boolean result = judgeTokenIfEffective(request, tokenString);
                    if(!result){
                        logger.warn("Token不合法,请求被过滤掉了");
                        throw new Exception("登录状态已失效,请重新登录");
                    }
                }
            }
        } else {
            logger.warn("请求的header是空");
            return null;
        }

        return chain.filter(exchange);
    }

    /**
     * 判断token是否有效
     * @param request 请求信息
     * @param token token信息
     * @return 返回判断结果
     */
    private Boolean judgeTokenIfEffective(ServerHttpRequest request, String token){
        Boolean result = Boolean.FALSE;

        if(!Strings.isNullOrEmpty(token)){
            String s = HttpUtils.sendGet(erpInterfaceAddressConfig.getGetInfo(), null, token);
            if (StringUtils.isEmpty(s)) {
                logger.error("调用接口异常,接口地址:[{}],接口返回信息为:[{}]", erpInterfaceAddressConfig.getGetInfo(), s);
            }else{
                try {
                    JSONObject json = JSON.parseObject(s);
                    if(json != null && !json.isEmpty()){
                        Integer status = (Integer) json.get(Constants.STATUS_);
                        if(status.equals(HttpStatus.OK.value())){
                            result = Boolean.TRUE;
                        }

                        JSONObject dataJson = json.getJSONObject(Constants.DATA);
                        if(dataJson != null && !dataJson.isEmpty()){
                            HttpHeaders headers = request.getHeaders();
                            headers = HttpHeaders.writableHttpHeaders(headers);
                            String id = dataJson.getString(Constants.ID);
                            String name = dataJson.getString(Constants.NAME);
                            headers.set(Constants.ID, id);

                            // 对汉字进行编码
                            String decode = URLEncoder.encode(name, "UTF-8");
                            headers.set(Constants.NAME, decode);
                        }
                    }

                }catch (Exception e){
                    logger.error("调用接口异常,Token:[{}]验证失败,接口地址:[{}],接口返回信息为:[{}]", token, erpInterfaceAddressConfig.getGetInfo(), s);
                }
            }
        }else{
            logger.info("当前请求token为空");
        }

        if(!result && !Constants.PROD_RUN_ENVIRONMENT.equals(runEnvConfig.getEnv())){
            logger.info("当前环境参数为:[{}],token无效时不拦截请求", runEnvConfig.getEnv());
            result = true;
        }

        return result;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}